package com.tzy.spi.aop;

import com.tzy.spi.aop.Interceptor.AroundMethodInterceptor;
import com.tzy.spi.aop.Interceptor.MethodInterceptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

public class AopInvoker {


    private InvokerChain chain;
    private Method method;
    private Object nativeBean;


    public AopInvoker(List<AopEnhancer.AspectHolder> list,Method method, Object nativeBean) {
        this.method=method;
        this.nativeBean=nativeBean;
        buildChain(list);
    }

    private void buildChain(List<AopEnhancer.AspectHolder> list) {

        this.chain=new InvokerChain(list, 0,method,nativeBean);
    }

    public Object invoke(Object[] args) throws Throwable {

        return chain.start(args);
    }

    static class InvokerChain {


        private CopyOnWriteArrayList<MethodInterceptor> localNodeList;

        private AroundMethodInterceptor around;

        private InvokerChain nextChain;

        private Method method;
        private Object nativeBean;

        private Annotation annotation;


        public InvokerChain(List<AopEnhancer.AspectHolder> list, int i,Method method,Object nativeBean) {

            this.method=method;
            this.nativeBean=nativeBean;

            AopEnhancer.AspectHolder aspectHolder = list.get(i);

            Map<String, MethodInterceptor> map = aspectHolder.getMap();

            this.annotation = aspectHolder.getAnnotation();
            //先获取 环绕 切面
            MethodInterceptor around = map.remove("around");
            if (around != null) {
                this.around = (AroundMethodInterceptor) around;
            }


            buildLocalNodeList(map);
            // 生成下一个切面的执行链
            if (i != list.size() - 1) {
                this.nextChain = new InvokerChain(list, i + 1,method,nativeBean);
            }
        }

        private void buildLocalNodeList(Map<String, MethodInterceptor> map) {
            CopyOnWriteArrayList<MethodInterceptor> list = new CopyOnWriteArrayList<>();

            addToList(list, map.getOrDefault("afterThrowing", null));
            addToList(list, map.getOrDefault("afterReturning", null));
            addToList(list, map.getOrDefault("after", null));
            addToList(list, map.getOrDefault("before", null));
            this.localNodeList = list;

        }

        private void addToList(List<MethodInterceptor> list, MethodInterceptor interceptor) {

            if (interceptor != null) {
                list.add(interceptor);
            }
        }


        public Object next(JoinPoint joinPoint) throws Throwable {


                int i = joinPoint.checkIndex();
                if(i ==this.localNodeList.size()){
                    //执行下一个
                    if(this.nextChain!=null){
                        return this.nextChain.start(joinPoint.getArgs());
                    }else {
                        try {
                            return this.method.invoke(this.nativeBean,joinPoint.getArgs());
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            throw e.getCause();
                        }
                    }
                }else {
                    MethodInterceptor interceptor = this.localNodeList.get(i);
                    return interceptor.toDo(joinPoint);
                }
        }

        public Object start(Object[] args) throws Throwable {

            String name = this.method.getName();
            JoinPoint joinPoint = new JoinPoint(this, args,name);

            if(around!=null){
                //执行around
                return around.toDo(joinPoint);
            }

            return next(joinPoint);
        }


        public Annotation getAnnotation() {
            return annotation;
        }
    }
}
